package com.yunban.project.template.service.impl;

import cn.hutool.core.lang.tree.Tree;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.yunban.project.api.text_check.domain.WordParseRecord;
import com.yunban.project.api.text_check.mapper.WordParseRecordMapper;
import com.yunban.project.api.text_check.service.impl.DeepSeekServiceImpl;
import com.yunban.project.api.text_check.utils.DeepSeekApplication;
import com.yunban.project.template.model.domain.PromptTemplate;
import com.yunban.project.template.model.dto.AIArticleFormatReq;
import com.yunban.project.template.service.ArticleAIFormatService;
import com.yunban.project.template.service.PromptTemplateService;
import com.yunban.project.template.utils.PromptUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import net.lab1024.sa.base.common.exception.BusinessException;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.reactive.function.client.WebClientResponseException;
import reactor.core.publisher.Flux;

import java.io.File;
import java.io.FileOutputStream;
import java.util.*;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @author: lijiajun
 * @date: 2025-03-03
 * @version: 1.0
 */
@Slf4j
@Service
public class DeepSeekFormatServiceImpl implements ArticleAIFormatService {

    private static final String DOC_TO_MARKDOWN_API = "http://192.168.1.88:5000/web-api/docx_to_markdown";
    private static final String DOC_TO_MARKDOWN_TEMP_DIR = "E://python-project/word-decode/temp/markdown";
    private static final String MARKDOWN_TO_HTML_API = "https://wss.lke.cloud.tencent.com/v1/qbot/chat/sse";
    private static final String BOT_APP_KEY = "hlHDsZMo";
    private static final List<String> FILE_EXT_LIST = List.of("docx");

    @Resource
    private RestTemplate restTemplate;

    @Resource
    private PromptTemplateService promptTemplateService;

    @Resource
    private WordParseRecordMapper wordParseRecordMapper;

    WordParseRecord saveWordParse(String filename) {
        WordParseRecord r = new WordParseRecord();
        r.setTaskId(UUID.randomUUID().toString());
        r.setFilename(filename);
        this.wordParseRecordMapper.insert(r);
        return r;
    }

    @Override
    public String format(AIArticleFormatReq req, MultipartFile file) {
        // 获取提示词
        if (CollectionUtils.isEmpty(req.getTypePromptIds())) {
            throw new BusinessException("请选择前端块类型提示词");
        }
        // 获取选中的提示词列表
        List<Tree<Long>> promptTemplateTree = this.promptTemplateService.getPromptTemplateTree(req.getTypePromptIds());
        // 构建提示词字符串
        String prompt = PromptUtils.buildPrompt(promptTemplateTree);
        String promptWord = this.word2Markdown(file) + "\n" + prompt;
        // TODO 这个代码暂时只是为了测试验证，后续需要进行优化
        WordParseRecord parseRecord = this.saveWordParse(file.getOriginalFilename());
        DeepSeekServiceImpl.TencentSSEChatReq tencentSSEChatReq = new DeepSeekServiceImpl.TencentSSEChatReq(promptWord, BOT_APP_KEY);
        // 创建 WebClient 实例
        WebClient webClient = WebClient.create();
        // 发送 POST 请求并处理 SSE 流
        Flux<String> sseStream = webClient.post()
                .uri(MARKDOWN_TO_HTML_API)
                .contentType(MediaType.APPLICATION_JSON)
                .bodyValue(tencentSSEChatReq)
                // 接收 SSE 流
                .accept(MediaType.TEXT_EVENT_STREAM)
                .retrieve()
                // 将响应体转换为 Flux<String>
                .bodyToFlux(String.class);
        // 订阅 SSE 流并处理每一条消息
        AtomicReference<String> response = new AtomicReference<>("");
        AtomicReference<String> htmlResult = new AtomicReference<>("");
        sseStream.subscribe(
                data -> {
                    try {
                        JSONObject sseJson = JSON.parseObject(data);
                        JSONObject payloadJson = JSON.parseObject(JSON.toJSONString(sseJson.get("payload")));
                        // 判断 payloadJson.get("is_final"); 是否是 true
                        Object isFinalObj = payloadJson.get("is_final");
                        if (ObjectUtils.isEmpty(isFinalObj)) {
                            return;
                        }
                        boolean isFinal = (isFinalObj instanceof Boolean) ? (Boolean) isFinalObj : Boolean.parseBoolean(isFinalObj.toString());
                        if (isFinal && "reply".equals(sseJson.get("type").toString())) {
                            String html = payloadJson.get("content").toString();
                            // 将 html 中的 \u003c  转为 <  TODO 响应的内容中存在转义字符
                            html = html.replaceAll("\\u003c", "<");
                            html = html.replaceAll("\\\\u003c", "<");
                            html = html.replaceAll("\\u003e", ">");
                            html = html.replaceAll("\\\\u003e", ">");
                            // 如果 html 前面是 ```html  或者 后面是  ```  那么则剔除
                            if (html.startsWith("```html") && html.endsWith("```")) {
                                html = html.substring(7, html.length() - 3);
                            }
                            html = html.trim();
                            htmlResult.set(html);
                            response.set(data);
                        }
                    } catch (Exception ex) {
                        log.error("Received SSE data error:{}", ex.getMessage());
                        log.error("Received SSE data error:{}", data);
                    }
                    log.debug("Received SSE data:{}", data);
                },
                error -> {
                    // 处理错误信息
                    if (error instanceof WebClientResponseException) {
                        log.error("HTTP error: " + ((WebClientResponseException) error).getStatusCode());
                        throw new BusinessException("HTTP error: " + ((WebClientResponseException) error).getStatusCode());
                    } else {
                        log.error("Error: " + error.getMessage());
                        throw new BusinessException("Error: " + error.getMessage());
                    }
                },
                () -> {
                    parseRecord.setHtml(htmlResult.get());
                    parseRecord.setResult(response.get());
                    this.wordParseRecordMapper.updateById(parseRecord);
                }
        );
        return parseRecord.getTaskId();
    }

    @Override
    public String word2Markdown(MultipartFile file) {
        // 设置请求头信息
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        DeepSeekApplication application = new DeepSeekApplication();
        // 获取文件信息
        DeepSeekApplication.FileInfo fileInfo = application.getFileExt(file);
        // 判断文件格式是否正确
        if (!FILE_EXT_LIST.contains(fileInfo.getExt())) {
            throw new BusinessException("目前仅支持docx格式");
        }
        File tempFile = null;
        try {
            // 将 MultipartFile 转换为临时文件
            tempFile = File.createTempFile("upload", "." + fileInfo.getExt());
            try (FileOutputStream fos = new FileOutputStream(tempFile)) {
                fos.write(file.getBytes());
            }
            // 创建请求体
            System.out.println(tempFile.getPath());
            Map<String, String> requestBody = new HashMap<>();
            requestBody.put("docx_path", tempFile.getPath());
            requestBody.put("output_dir", DOC_TO_MARKDOWN_TEMP_DIR);
            HttpEntity<Map<String, String>> requestEntity = new HttpEntity<>(requestBody, headers);
            // 发送POST请求并接收响应
            ResponseEntity<String> response = this.restTemplate.exchange(
                    DOC_TO_MARKDOWN_API,
                    HttpMethod.POST,
                    requestEntity,
                    String.class);
            if (ObjectUtils.isEmpty(response)) {
                throw new BusinessException("doc2markdown error: response is null");
            }
            if (response.getStatusCode() == HttpStatus.OK) {
                return response.getBody();
            } else {
                throw new BusinessException("Failed to upload file. HTTP Status Code: " + response.getStatusCode().value());
            }
        } catch (Exception ex) {
            log.error("doc2markdown error", ex);
            log.error("doc2markdown error: {}", ex.getMessage());
            throw new BusinessException("doc2markdown error:" + ex.getMessage());
        }
    }
}
